home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / audio / sonic / README < prev    next >
Encoding:
Text File  |  1994-08-02  |  35.6 KB  |  687 lines

  1.  
  2.              ~4Dgifts/toolbox/src/exampleCode/audio/sonic README
  3.  
  4.      The contents of this README is an article that originally appeared in 
  5.      the March/April 1993 Issue of the Silicon Graphics support magazine 
  6.      "Pipeline", Volume 4, Number 2.  It is the companion to the sonic.c 
  7.      program example.  
  8.  
  9.  
  10.  
  11.               ADDING AUDIO TO AN EXISTING GRAPHICS APPLICATION.
  12.  
  13.    
  14.    This article will discuss some of the common issues and techniques
  15.    programmers encounter when adding audio to an existing graphics 
  16.    application.  Here, we'll look at an example program, sonic.c,
  17.    that illustrates some of the techniques you will want to use -
  18.    spawning a separate audio process, initializing the audio port,
  19.    reading audio files, playing one-shot vs. continuous audio, 
  20.    mixing sounds, using stereo audio effectively in 3D programs, and
  21.    dealing with CPU demands of an audio process that cause undesirable
  22.    clicking.
  23.    
  24.    
  25.    HARDWARE AND SOFTWARE REQUIREMENTS
  26.    
  27.    Currently, under version 4.0.5F of IRIX, audio development can be done 
  28.    on any Iris Indigo platform - R3000 or R4000, from Entry to XS to
  29.    Elan graphics.  This class of machine has standard digital audio 
  30.    hardware, and is able to run the Digital Media Development Option, 
  31.    for which this example program was written.
  32.    
  33.    The Digital Media Development Option is a relatively new
  34.    software package which consists of Application Programmers'
  35.    Interfaces (API's) for digital audio (libaudio), the starter 
  36.    video (libsvideo), MIDI (libmidi), the AIFF-C audio file 
  37.    (libaudiofile), CD and DAT drive audio access (libcdaudio and 
  38.    libdataudio).  This package comes with the Digital Media 
  39.    Programmers Guide to aid in learning about the new libraries. 
  40.    The Digital Media Development Option requires version 4.0.5F 
  41.    of the operating system and will run on any Iris Indigo system.
  42.    
  43.    Note: Some 4D35's have the ability to produce the same quality
  44.    sound as that of the Indigo. Programming audio on the 4D35's
  45.    at version 4.0.5A or earlier of the operating system uses
  46.    the same Audio Library that the Indigo uses.  The Digital
  47.    Media Development Option, unfortunately, will currently not run 
  48.    on a 4D35 system, as IRIX version 4.0.5F is not qualified for 
  49.    installation on 4D35's.
  50.    
  51.    For this programming example, you'll need audiodev.sw from the 
  52.    Digital Media Development Option, which contains the libraries 
  53.    for the Audio Library (AL) and the Audio File Library (AF).  
  54.    These subsystems comprise the Audio Development Environment, 
  55.    version 2.0 and are provided with the Digital Media Development 
  56.    Option.  (These are the successors to the Audio Development 
  57.    Environment originally provided with the Iris Development Option, 
  58.    in version 4.0.1 of the operating system.)  
  59.    
  60.    This example also makes use of some of the Prosonus sound files
  61.    found in the /usr/lib/sounds/prosonus directory.  These audio
  62.    files originate from the audio.data.sounds subsystem from the
  63.    4.0.5F Update to the operating system.  Check the output of the 
  64.    IRIX command versions to make sure these subsystems are installed 
  65.    on your machine.
  66.    
  67.    
  68.    ABOUT THE EXISTING GRAPHICS PROGRAM
  69.    
  70.    The structure of the graphics program which sonic is based on is
  71.    typical of Graphics Library (GL) programs and lends itself easily
  72.    for conversion to mixed-model programming.  Windowing and input
  73.    routines are handled in the main body of the program. GL
  74.    routines that need to be called once are handled in the sceneinit() 
  75.    function, rendering is handled by the drawscene() function, and 
  76.    animation is handled by the movesphere() function. 
  77.    
  78.    The program itself models a stationary observer inside a colored 
  79.    cube with a sphere that flies around the inside of the cube, 
  80.    bouncing off walls.  The observer can rotate (but not translate) 
  81.    the camera's viewpoint by moving the mouse to see different views from 
  82.    inside the cube.  The left mouse button starts and stops the motion 
  83.    of the sphere.
  84.    
  85.    We'll be adding two different sounds to create a stereo audio
  86.    environment for the application.
  87.    
  88.    The first sound will be an example of continuous audio - a sound the 
  89.    sphere continuously makes as it moves.  This is analogous to the 
  90.    constant engine noise of a plane.  As the sphere gets closer to the 
  91.    observer, its intensity increases or decreases, depending on the 
  92.    distance from the observer.  As the observer's viewpoint rotates, the 
  93.    audio "location" (as perceived through headphones) will also change; 
  94.    that is, as the sphere passes the observer from right to left, so 
  95.    will the sound of the sphere.  
  96.    
  97.    The second sound will be a one-shot sound - a sound the sphere
  98.    makes under the condition of it's direction change, either from
  99.    bouncing off a wall, or the user toggling the sphere's motion
  100.    with the left mouse button.  This is analogous to the sound of 
  101.    a missile hitting a target.  This sound is also effected by the 
  102.    orientation of the observer and distance from the sphere's event.
  103.    
  104.    
  105.    AUDIO LIBRARY BASICS
  106.    
  107.    The Audio Library (AL) itself divides the task of sending audio
  108.    samples to the hardware into two main areas of control - 
  109.    devices and ports. 
  110.    
  111.    The audio device controls the input/output volume, input source, 
  112.    input/output sampling rate, etc. AL functions exist to control
  113.    the default audio device, however, it is considered "polite" audio
  114.    etiquette to let the user control these via apanel.  Apanel itself
  115.    is just an AL program that controls the default audio device with 
  116.    a graphical user interface.  It is possible for a program that
  117.    asserts its own audio device parameters to modify another audio 
  118.    program's device settings.  The Indigo's default audio device supplies
  119.    up to four audio ports, either stereo or mono, for AL applications 
  120.    to use.
  121.    
  122.    An audio port is an entity that an AL program reads samples from
  123.    or writes to the audio device.  A port can be thought of as a queue of 
  124.    sound samples, where the AL programmer has control over only one end 
  125.    of the queue.  Thus, a program that received audio input would read 
  126.    samples in from one end of this queue, with the audio device supplying 
  127.    the samples from an input source such as a microphone.  Conversely, 
  128.    a program that generated audio output would supply data for one 
  129.    end of the queue, and the audio device would send the queued
  130.    samples to the audio outputs, such as the Indigo's speaker, or a set 
  131.    of headphones. An audio program that did both audio input and audio 
  132.    output would require the use of two audio ports and associated queues.
  133.    We'll discuss the size of this queue a little later.
  134.    
  135.    A port can be configured to utilize an audio sample datatype that 
  136.    best suits the application with the ALsetsampfmt() function. 
  137.    Sample data can be represented by a 2's complement integer or single 
  138.    precision floating point.  Integer data can be 8, 16, or 24 bits wide.  
  139.    Sample width is controlled by the ALsetwidth() command.  Sample width 
  140.    does not represent the maximum amplitude of the input or output of an 
  141.    audio signal coming into or out of the audio jacks.  If this were true, 
  142.    one could incorrectly imply that an audio port with a sample width of 
  143.    24 could have a louder dynamic range than an audio port of width 8.  
  144.    Instead, sample width represents the degree of precision to which the 
  145.    full scale range of an input or output signal will be sampled.  That 
  146.    is, if the maximum value for an 8-bit sample is 127, or 2^7 - 1, the 
  147.    signal level represented by this sample could also be represented by a 
  148.    16-bit sample whose value is 32767, or 2^15 - 1, or a 24 bit sample 
  149.    whose value is 2^23 -1.  For floating point data, sample width is 
  150.    always the same, but having MAXFLOAT as the maximum amplitude is often 
  151.    impractical.  The AL function ALsetfloatmax() allows the programmer 
  152.    to specify an appropriate maximum value for their own data when the 
  153.    sample format is designated to be floats.  Dynamic range of the data is 
  154.    required to be symmetrical and centered around the value 0, so the 
  155.    absolute value of the minimum amplitude value is always equal to the 
  156.    maximum amplitude.
  157.    
  158.    Ports can be configured to accept either stereo or monaural sample 
  159.    streams with the ALsetchannels() call.  Stereo sample streams are 
  160.    implemented as interleaved left-right pairs, where even numbered s
  161.    amples represent the left channel and odd numbered samples represent 
  162.    the right channel.  As one might expect, a stereo sample buffer will 
  163.    be twice as big as a mono sample buffer 
  164.    
  165.    
  166.              Array index    0   1   2   3   4   5
  167.                           -------------------------
  168.    Audio Sample Array     | L | R | L | R | L | R | ... 
  169.                           -------------------------
  170.                             \  /
  171.                              \/
  172.                              A stereo sample pair to be input 
  173.                              or output simultaneously
  174.    
  175.    
  176.    CREATING A SEPARATE PROCESS FOR AUDIO
  177.    
  178.    It's nice to be able to keep audio and graphics separate. 
  179.    For this example program, the audio we're producing is in reaction
  180.    to events being handled by the section of the program responsible
  181.    for graphics.  The graphics process controls the motion and position 
  182.    of the sphere, as well as the orientation of the observer.  These are
  183.    all aspects we'd like our audio display to reflect, but not control.  
  184.    Creating a completely separate process to handle the audio has one 
  185.    main benefit - it provides enough independence of the audio aspects 
  186.    of the application so that audio performance is not degraded when the
  187.    controlling process contends for graphics resources.  This independence 
  188.    can be achieved with the sproc() function and can be enhanced by 
  189.    raising the priority of the audio process through IRIX scheduling 
  190.    control which will be discussed later.
  191.    
  192.    
  193.    The sproc() function is the mechanism for spawning a separate audio 
  194.    child process from our parent process which handles the graphics.  
  195.    Sproc() is nice and easy for our purposes.  It says, "Create a separate 
  196.    process and have it start out by executing the function I tell you."   
  197.    The original process will continue merrily on its way.
  198.    Besides a starting-point function, sproc() takes another argument
  199.    which tells how the parent and child processes should share data.
  200.    The sonic program starts the newly created child process at
  201.    the audioloop() function.  The PR_SALL argument that sonic uses
  202.    tells the parent and child to share nearly everything.  We're
  203.    mostly interested that the parent and child processes share virtual 
  204.    address space and that the data in this address space is consistent 
  205.    between them.  This means that the audio process will get to look at 
  206.    how the graphics process changes the values of the "interesting" variables. 
  207.    This also means that if either the graphics process or the audio 
  208.    process change the value of a variable, the other will know about it 
  209.    immediately.  Having the variables shared becomes the mechanism of 
  210.    communication between the two processes.  See the man page for sproc() 
  211.    for intimate details.
  212.    
  213.    In general, it is not recommended that two separate processes both 
  214.    call GL functions pertaining to the same connection to the graphics 
  215.    pipe.  To avoid encouraging graphics calls within the audio process,
  216.    and to establish a logical separation of the graphics and audio 
  217.    processes, the sproc() is called before winopen().
  218.    
  219.    
  220.    INITIALIZATION
  221.    
  222.    The main task of any AL program is to read or write audio data to or 
  223.    from an audio port fast enough so that the user percieves the desired 
  224.    audio effect without interruption, i.e. the sample queue is never
  225.    completely empty.  Any AL program that performs audio processing for 
  226.    output will have a code structure that looks something like the pseudo-
  227.    code below.  The elements of the pseudo-code can be seen in sonic.c. 
  228.    
  229.    
  230.    #include <audio.h>
  231.    
  232.    ALport audioport;
  233.    ALconfig audioconfig;
  234.    
  235.    /* Audio initializiation */
  236.    audioconfig = ALnewconfig();    /* New config structure */
  237.    
  238.    ...    /* Set up configutation */
  239.           /* of audio port.       */
  240.    
  241.    ALsetsampfmt( audioconfig, AL_SAMPFMT_FLOAT);
  242.    
  243.    ...
  244.    
  245.    audioport = ALopenport("port name","w",audioconfig);   
  246.        /* Open audio port */
  247.    
  248.    /* Audio main loop */
  249.    while( ! done ) {
  250.        process_audio();            /* Compute samples */
  251.        ALwritesamps(audioport, samplebuffer, bufferlength);
  252.                /* Output samples to port */
  253.    }
  254.    
  255.    /* Audio shut down */
  256.    ALfreeconfig(audioconfig);
  257.    ALcloseport(audioport);             /* Close audio port */
  258.    
  259.    
  260.    Notice that port configuration information is put into the Audio 
  261.    Library structure ALconfig which is then passed as a parameter to the 
  262.    ALopenport() function.  This implies that if we wish to change to 
  263.    or mix different sample formats of our data, or any other aspect
  264.    of the audio port's configuration, we will either need to open 
  265.    another audio port or convert all sample data to one common format.
  266.    
  267.    Choosing the size of the sample queue for the configuration of
  268.    the audio port is very important in applications such as this
  269.    where audio dynamics are constantly changing.  The AL function
  270.    ALsetqueuesize() provides the means of control here.  The Audio
  271.    Library currently allows the minimum queue size to be 1024 samples
  272.    (or 512 stereo sample pairs) for the floating point audio port we're 
  273.    using in sonic.  It is not a bad idea to set the size of your sample 
  274.    queue to be about twice that of the number of samples you are 
  275.    processing.  This gives some leeway for audio processing time to 
  276.    take a little longer than expected if the audio device occasionally 
  277.    drains the audio sample queue too fast, but also provides room enough 
  278.    to send a fresh batch of samples if the queue is draining too slow.
  279.    However, it is possible for audio latency to increase with a queue 
  280.    larger than needed.  Stereo sample queues need to be kept at even 
  281.    lengths so that the proper sense of stereo separation will not be 
  282.    switched for stereo sample pairs in every second call to ALwritesamps().
  283.    
  284.    Try changing the #define BUFFERSIZE to 512.  Recompile and run the
  285.    sonic program.  You might notice that, in general, the audio sounds
  286.    scratchier.  This can be because the actual processing of the
  287.    audio is taking longer than the hardware's playing of the audio.
  288.    In other words, it's possible for the sample queue to be
  289.    emptied faster than it's filled.  A small sample buffer may
  290.    provide lower latency between updates to the output sample stream,
  291.    but you need to keep the output continuous to keep it from producing
  292.    undesirable clicks.  On the other hand, a larger sample buffer
  293.    will increase latency and is apt to detract from the overall
  294.    audio experience.  Change the BUFFERSIZE to 44100 to hear the
  295.    effects of high latency.  Notice the stereo placement of the
  296.    sphere seems choppier.  A BUFFERSIZE of 4000 seems to do a good
  297.    job for sonic - enough to keep the audio process busy, without
  298.    detracting from the user's interaction with the application. 
  299.    You'll have to find your own happy medium for your own application.  
  300.    (Don't forget to change BUFFERSIZE back!)
  301.    
  302.    Thirty-two bit floats are used in this example as the base sample 
  303.    format to which all sample data will be converted.  They provide a 
  304.    convenient means for multiplication without type casting in the time 
  305.    critical sections of the code that do the actual sound processing.  
  306.    Also, specifying a maximum amplitude value of 1.0 can provide for a 
  307.    handy normalization of all sound data, especially if some waveforms 
  308.    are to effect the behavior of other waveforms (like an envelope 
  309.    function).
  310.    
  311.    
  312.    READING AUDIO FILES
  313.    
  314.    The sounds used in sonic are read in from the routine init_sound().
  315.    It uses the Audio File Library (AF) to read AIFF (.aiff suffix)
  316.    and AIFF-C (.aifc suffix) audio files.  To provide a common
  317.    ground for some nice sounds, sonic uses sounds from the 
  318.    /usr/lib/sounds/prosonus directory.  You should be able
  319.    to change which sounds are used by editting the BALLFILENAME
  320.    and WALLFILENAME #defines.  
  321.    
  322.    The AF has two main structures to deal with. An audio file
  323.    setup, which is used mainly for writing audio files, is needed
  324.    to get an audio file handle with the AFopenfile() routine.
  325.    For more information on audio file setups, see the Digital
  326.    Audio and MIDI Programming Guide that comes with the Digital
  327.    Media Development Option.  
  328.    
  329.    An audio file contains a lot of information about the data inside.
  330.    Sample format, sample width, number of channels, number of sample 
  331.    frames, as well as the sound data itself, are about all this example 
  332.    needs.  It is possible to get information from an AIFF-C file that 
  333.    describes looping points, pitch and suggested playback rates (if they 
  334.    are provided in the file).  See the Audio Interchange File Format
  335.    AIFF-C specifications and the Digital Audio and MIDI Programming 
  336.    Guide for more details on what can be stored in an AIFF-C file.
  337.    
  338.    When reading in audio files into your program you may find it
  339.    necessary to convert the sample data into a format that's
  340.    better suited to your application.  Most of the prosonus
  341.    sounds are in 16-bit 2's complement format.  Any file that is 
  342.    not in this format produces an error message, as an appropriate 
  343.    conversion to floating point for other formats was not implemented
  344.    for the sake of simplicity.  Since the program is dealing with 
  345.    point sources of audio, a stereo sound source is inappropriate.  
  346.    Thus, the conversion to floating point also includes a conversion 
  347.    from stereo to mono.  In this conversion, only the left channel 
  348.    is used.  A summation or average of the left and right channels 
  349.    could have been just as easy to implement as our conversion from 
  350.    stereo to mono.
  351.    
  352.    
  353.    ONE-SHOT & CONTINUOUS AUDIO
  354.    
  355.    Many applications add audio simply by generating a system() call
  356.    to the IRIX commands playaiff or playaifc utilities.  For some 
  357.    applications this is enough to add a little bit of audio, but this 
  358.    approach can be limiting in that your audio is only as effective as 
  359.    the sound file you play.  This solution can be a quick and dirty way 
  360.    to do one-shot audio - audio that can be triggered by a single event, 
  361.    like a car crashing into a tree, or the sound of a ball hitting a 
  362.    tennis racket  - but it comes with the penalty of losing interaction 
  363.    with the sound.  Sometimes interaction is not a concern for these 
  364.    types of sounds.
  365.    
  366.    Continuous audio is different than one-shot audio in that it describes
  367.    a sound that's always present to a certain degree, like the sound
  368.    of a jet engine for a flight simulator, or the sound of crickets for
  369.    ambience.
  370.    
  371.    In an application where the audio output changes continually with 
  372.    the user's input, it can be convenient to approach preparing the
  373.    samples in chunks of equal amounts of time.  Changes in audio
  374.    dynamics will happen on sound buffer boundaries and multiple sounds
  375.    will need to be mixed together to form one sample stream.
  376.    
  377.    Processing continuous audio is fairly straightforward. Sounds can
  378.    be longer than the buffer that is being used for output, therefore
  379.    an index needs to be kept on where the continuous sound left off
  380.    for the next time around.  Looping can be achieved by a test to
  381.    see if the index goes out of bounds or (even better) by a modulo
  382.    function.  
  383.    
  384.    Processing one-shot audio is similar to continuous audio, with the
  385.    additional criterion that the program needs to keep track of
  386.    information such as when to start the sound, when to continue 
  387.    processing the sound (if the one-shot sound is longer than the 
  388.    audio buffer), and when to stop processing the sound.  Sonic defines 
  389.    the variable "hit" to describe if there is NO_HIT on the wall (no 
  390.    processing needed), if the sphere JUST_HIT the wall (start processing),
  391.    or if the wall has BEEN_HIT by the sphere (continue processing).
  392.    While it is the graphics process that initiates the one-shot
  393.    sound by changing the state of the "hit" variable, it is the 
  394.    audio process that acknowledges the completion of the one-shot 
  395.    sound by changing the state of the variable to indicate completion
  396.    
  397.    
  398.    SIMPLE CALCULATIONS FOR SPATIALIZATION OF AUDIO
  399.    
  400.    Sonic uses some _very_ basic calculations that attempt a 3-D audio 
  401.    simulation of the sphere/room environment.  Amplitude and left-right
  402.    balance relative to the observer are the only audio cues that are 
  403.    calculated in this example.  You will notice that if the sphere is in 
  404.    close proximity to the observer, the amplitude of the sounds eminating 
  405.    from the sphere are louder than they would be if the sphere were in the 
  406.    distance.  You will also notice that as the orientation of the 
  407.    observation point is changed, the left-right location of the sound 
  408.    changes accordingly.  After a bit of playing with sonic, you may notice 
  409.    that your sense of whether the sound is coming from in front of you or 
  410.    in back of you depends on the visual cue of the sphere being within the 
  411.    field of view of the graphics window.  It is audibly obvious that sonic
  412.    does not attempt any calculations for top-bottom or front-back 
  413.    spatialization.  With a two channel (left-right) audio display system,
  414.    such as a pair of headphones,  anything other than a sense of left-right
  415.    balance is computationally difficult to simulate, so we'll ignore it here.
  416.    
  417.    The first thing we will need to calculate will be the position of 
  418.    the sphere, described by the coordinates <sphx,sphy,sphz>, relative 
  419.    to the orientation of observer, described by he angles rx and ry.  
  420.    In order to do this correctly, we'll need to borrow from the computer 
  421.    graphics concept of viewing transformations to compute which direction 
  422.    the observer should perceive the sound to be coming from.  Using these 
  423.    relative coordinates we can first compute the overall amplitude of the 
  424.    sound to reflect the distance of the sound, and then compute the 
  425.    amplitude of the sound for each speaker to reflect the location of the 
  426.    sound in the audio field.
  427.    
  428.    It is the responsibility of the graphics process to update the 
  429.    coordinates of the moving sphere, <sphx,sphy,sphz>, and the angles 
  430.    describing the orientation of the observer, rx and ry.  Since location 
  431.    in the audio display needs to correspond to location in the graphics 
  432.    display, we need to follow the order that modelling and viewing 
  433.    transformations are performed in the graphics process.  In the graphics
  434.    process, the GL commands
  435.    
  436.        rot(ry, 'y');
  437.        rot(rx, 'x');
  438.    
  439.    correspond to the following matrix equation for each point that is passed
  440.    through transformation matrix. (Remember that GL premultiplies its
  441.    matrices!)
  442.    
  443.  
  444.   |      |
  445.   | relx |
  446.   |      |
  447.   | rely |
  448.   |      |  =   <sphx,sphy,sphz> * Rot (radx) * Rot (rady)   = 
  449.   | relz |                            x            y
  450.   |      |
  451.   |  1   |
  452.   |      |
  453.  
  454.  
  455.                    |                          |   |                          |
  456. <sphx,sphy,sphz> * | 1      0         0     0 |   | cos(rady) 0 -sin(rady) 0 |
  457.                    |                          |   |                          |
  458.                    | 0  cos(radx) sin(radx) 0 |   |     0     1      0     0 |
  459.                    |                          | * |                          |
  460.                    | 0 -sin(radx) cos(radx) 0 |   | sin(rady) 0  cos(rady) 0 |
  461.                    |                          |   |                          |
  462.                    | 0      0         0     1 |   |     0     0      0     1 |
  463.                    |                          |   |                          |
  464.  
  465.  
  466.                    |                                                       |
  467. <sphx,sphy,sphz> * |     cos(rady)           0          -sin(rady)       0 |
  468.                    |                                                       |
  469.                    | sin(radx)*sin(rady)  cos(radx) sin(radx)*cos(rady)  0 |
  470.                    |                                                       |
  471.                    | cos(radx)*sin(rady) -sin(radx) cos(radx)*cos(rady)  0 |
  472.                    |                                                       |
  473.                    |          0              0              0            1 |
  474.                    |                                                       |
  475.  
  476. or
  477.  
  478. |      |   |                                                                   |
  479. | relx |   | sphx*cos(rady)+sphy*sin(radx)*sin(rady)+sphz*cos(radx)*sin(rady)  |
  480. |      |   |                                                                   |
  481. | rely |   | sphy*cos(radx) - sphz*sin(radx)                                   |
  482. |      | = |                                                                   |
  483. | relz |   | -sphx*sin(rady)+sphy*sin(radx)*cos(rady)+sphz*cos(radx)*cos(rady) |
  484. |      |   |                                                                   |
  485. |  1   |   |                                1                                  |
  486. |      |   |                                                                   |
  487.  
  488.  
  489.    Where sphx, sphy and sphz are the world coordinates of the sphere,
  490.    relx, rely and relz are the coordinates of the sphere relative to the 
  491.    observer, and radx and rady describe the rotations (in radians) about 
  492.    the x and y axes, respectively.
  493.    
  494.    The overall amplitude of a sound can give some impression of a sense of 
  495.    distance.  Each buffer of sound to be processed at any given slice of 
  496.    time is multiplied by a amplitude scaling value that is based on the 
  497.    3-D distance of the sphere relative to the observer.  That amplitude 
  498.    scaling value is approximated with an inverse-square of the distance 
  499.    from the center of the observer's head to the center of the sphere by 
  500.    the equation
  501.    
  502.    amplitude = 1.0 / (distance*distance + 1.0)
  503.    
  504.    The 1.0 added to the square of the distance in the denominator is to 
  505.    insure we get a valid scaling value between 0.0 and 1.0, even when the 
  506.    sphere is right on top of the observer at a distance of 0.0.
  507.    
  508.    Since the most common method of audio display is either a set of 
  509.    headphones or a pair of speakers, sonic only attempts to simulate 
  510.    a sense of left and right.  It may be possible to simulate a sense 
  511.    of top and bottom, as well as a sense of front and back, perhaps 
  512.    with a combination of filters and echoes, however this can be 
  513.    computationally expensive and quite complex. Thus, for the sake
  514.    of this simple example, sonic ignores these techniques for aurally 
  515.    simulating orientation.
  516.    
  517.    One way of considering how left-right orientation is perceived 
  518.    is to think of a listener interpretting a given stereo sound 
  519.    as having some angle from what the listener considers to be directly 
  520.    in front of them.  The balance knob on an everyday stereo controls this 
  521.    sense of left-right placement.  We'll use the term "balance" to describe 
  522.    the listener's perceived sense of left and right.  
  523.    
  524.    We can think of balance being on a scale from 0.0 to 1.0, where
  525.    0.0 is full volume right ear only, 1.0 is full volume left ear only, 
  526.    and 0.5 is the middle point with both ears at half volume.
  527.    Now we need some way of relating this 0.0 - 1.0 scale for balance to 
  528.    the general orientation of the sonic sphere with respect to the observer.  
  529.    
  530.    For convenience, we can think of our sound space for computing balance
  531.    to be 2 dimensional.  Since we're not worrying about aurally simulating 
  532.    top-bottom orientation, our 3-D space for graphics can be projected into 
  533.    the 2-D plane for our audio, the x-z plane, where the listener's perception 
  534.    of straight ahead is in the positive z direction (0.5 on our balance scale), 
  535.    full right extends in the positive x direction (balance of 0.0), full left 
  536.    extends in the negative x direction (a balance of 1.0).
  537.    
  538.                         half left/half right
  539.                           balance = 0.5
  540.                             angle = PI/2
  541.    
  542.                                  +z
  543.                                         O sphere
  544.                                  ^     /   
  545.                                  |    /
  546.                              _/-----\_     perceived angle
  547.                             /    |  / \
  548.                            /     | /   \
  549.        full left           |     |/)   |             full right
  550.     balance = 1.0   -x  ---|-----+-----o---->  +x   balance = 0.0
  551.       angle = PI           |     |     |(1.0,0.0)     angle = 0 
  552.                            \     |     /
  553.                             \_   |   _/
  554.                               \-----/
  555.                                  |
  556.                                  |
  557.    
  558.                       observer at origin, (0.0,0.0)
  559.    
  560.     
  561.    The angle that is to be interpretted by the listener and mapped to our 
  562.    scale for balance is the angle that is made with the vector extending 
  563.    from the center of the observer to center of sphere and the line that 
  564.    goes through both ears of the observer, (or the line z=0).  A simple way 
  565.    of mapping this angle to our 0.0 to 1.0 scale for balance would be the
  566.    arccosine function.  An angle of PI radians could map to our scale for 
  567.    balance at 1.0 - all the way left; an angle of 0 radians could map to 
  568.    our scale for balance at 0.0 - all the way right.  
  569.    
  570.    To map our vector from observer to sphere onto the unit circle required
  571.    for the arccosine function, we need to normalize the vector, so the 
  572.    argument to the arccosine function is the distance which the normalized 
  573.    vector from the observer to the center of the sphere, extends in the x 
  574.    direction.  So the equation sonic uses to compute left-right balance is 
  575.    
  576.        balance = acos( relx/distance) / PI
  577.    
  578.    
  579.    Other spatialization techniques with which you may wish to experiment 
  580.    may be some sort of phase calculation between both ears.  Adding 
  581.    reverb or delay that interacts with the sounds can add a sense of 
  582.    depth to the room.  Filters for top-bottom or front-back transfer 
  583.    functions could also be implemented.  However, none of these would 
  584.    come without adding computational complexity and an extra tax on CPU .
  585.    
  586.    
  587.    SENDING MORE THAN ONE SOUND TO A SINGLE AUDIO PORT
  588.    
  589.    Since the continuous audio is the always the first sound to be 
  590.    processed in sonic, no mixing is needed - samples are just copied 
  591.    into the buffer - but the one-shot samples need to be mixed with 
  592.    those that are already there.  Processing the one-shot sound always 
  593.    comes after the processing of the continuous sound in this application. 
  594.    A simple mixing operation can be written by just summing the samples
  595.    that are currently in the sample buffer to those that are about
  596.    to be mixed in.  
  597.    
  598.    Beware!  
  599.    
  600.    Clipping, perceived as sharp clicks, can occur if the sum of the two 
  601.    samples exceeds the value for maximum or minimum amplitude.  To 
  602.    prevent this undesirable effect, weighted averages for the samples to 
  603.    be summed can be used.  If a maximum of two sounds will be mixed, 
  604.    weighting each sound by 1/2 before summation will guarantee no 
  605.    clipping.  For 3 sounds use 1/3, etc.  This guarantee does not 
  606.    come without a trade-off, though.  You'll have to decide on yet 
  607.    another happy medium, this time between the clipping that a straight 
  608.    summation can produce and the general decrease in overall volume 
  609.    that weighted averages can produce. 
  610.    
  611.    Now that that we've done all our processing, it's time to send the 
  612.    sample buffer to the audio port for output using the ALwritesamps() 
  613.    call.  If there is not enough room in the sample queue for the 
  614.    entire sample buffer to fit, ALwritesamps() will block until there 
  615.    is enough space.  It is possible to get feedback on the progress of 
  616.    the draining (or filling if the audio port is configured for input
  617.    rather than output) of the queue by the audio device.  The 
  618.    ALgetfillable() and ALgetfilled() functions can be used to give an
  619.    idea of how many samples are left to go before sufficient space is 
  620.    available in the queue.  The sonic audio process calls sginap()
  621.    to give up the CPU if it needs to wait for room in the sample
  622.    queue.
  623.    
  624.    
  625.    COMMON SOURCES OF AUDIO CLICKS AND DISCONTINUITY
  626.    
  627.    Discontinuities in audio can arise as sharp clicks or complete
  628.    dropouts of sound.  In general, greater attention to smooth audio 
  629.    performance should be paid rather than worrying about graphics
  630.    performance and frame rates.  A sudden, unexpected loud click is much 
  631.    more irritating to an end-user than graphics that aren't going as
  632.    fast as they could.  Here are some common causes and suggested
  633.    workaround for discontinuities in audio:
  634.    
  635.    1) Audio processing is feeding the output sample buffer slower than
  636.    the audio device is draining the samples.  As discussed earlier,
  637.    this usually happens with small sample queue sizes.  Increasing
  638.    the queue size for your audio port _can_ help here.  Keep in
  639.    mind that extensive audio processing may bog down the CPU,
  640.    in which case, your audio process may never be able to keep
  641.    the sample queue filled adequately.
  642.    
  643.    2) Clipping from mixing sounds.  The "Beware!" from the text above.
  644.    See the section on "Sending more than one sound to an audio port"
  645.    
  646.    3) Buffer boundaries in interactive audio.  In the graphics process,
  647.    motion is only as continuous as the frame rate will dictate.
  648.    If your audio process is like sonic, audio dynamics can change 
  649.    from iteration to iteration of the sound processing.  Like
  650.    the frame rate in graphics, the continuity of the audio
  651.    is only as smooth as the continuity of the data that changes
  652.    the dynamics of the audio.  This source of discontinuity
  653.    tends to be more subtle.  Perception of this type of discontinuity 
  654.    can be influenced by decreasing the size of the audio buffer.
  655.    
  656.    4) Other processes are contending for the same CPU.  Indigos are single
  657.    processor machines and all other processes need to use the same
  658.    CPU as yor audio process.  The audio process can lose CPU time due 
  659.    to IRIX scheduling of other processes (including the graphics 
  660.    process).  One solution is to use the schedctl() function.  Upon 
  661.    entering the audioloop() function, sonic's audio process tries to 
  662.    schedule itself as a high, non-degrading priority to put its priority
  663.    above other user processes contending for the CPU.  To gain the high,
  664.    non-degrading priority the process must be run as the super-user.
  665.    Sonic continues on if the use of schedctl() fails.  See the man 
  666.    page for schedctl() for the gory details.
  667.    
  668.    5) The audio process is swapping out of virtual memory.  Just because
  669.    a process has a high priority doesn't mean parts of virtual
  670.    memory will not be swapped out to disk.  You can use the
  671.    plock() command to combat this.  Sonic attempts to lock
  672.    the process into memory just after attempting the schedctl()
  673.    command.  The argument of PROCLOCK indicates that all aspects
  674.    of the program are to be locked into memory if possible. 
  675.    Like schedctl(), plock() will only be successful if the 
  676.    effective user id is that of the super-user. Sonic continues on 
  677.    if the use of plock() fails.  See the man page for plock() for 
  678.    the details.
  679.    
  680.    Not everyone that runs the program will have access to the
  681.    super-user account.  You can insure that users can execute
  682.    the program as super-user to take advantage of a high,
  683.    non-degrading priority and locking the process into memory
  684.    by changing the ownership of the executable to root, and by 
  685.    changing the permissions to set-user-id on execution. See the 
  686.    man page for chmod for more details.
  687.